#include "scanner.h"
#include "token.h"

namespace YAML {
struct Mark;

Scanner::SimpleKey::SimpleKey(const Mark& mark_, std::size_t flowLevel_)
    : mark(mark_), flowLevel(flowLevel_), pIndent(nullptr), pMapStart(nullptr),
      pKey(nullptr) {}

void Scanner::SimpleKey::Validate() {
    // Note: pIndent will *not* be garbage here;
    //       we "garbage collect" them so we can
    //       always refer to them
    if (pIndent)
        pIndent->status = IndentMarker::VALID;
    if (pMapStart)
        pMapStart->status = Token::VALID;
    if (pKey)
        pKey->status = Token::VALID;
}

void Scanner::SimpleKey::Invalidate() {
    if (pIndent)
        pIndent->status = IndentMarker::INVALID;
    if (pMapStart)
        pMapStart->status = Token::INVALID;
    if (pKey)
        pKey->status = Token::INVALID;
}

// CanInsertPotentialSimpleKey
bool Scanner::CanInsertPotentialSimpleKey() const {
    if (!m_simpleKeyAllowed)
        return false;

    return !ExistsActiveSimpleKey();
}

// ExistsActiveSimpleKey
// . Returns true if there's a potential simple key at our flow level
//   (there's allowed at most one per flow level, i.e., at the start of the flow
// start token)
bool Scanner::ExistsActiveSimpleKey() const {
    if (m_simpleKeys.empty())
        return false;

    const SimpleKey& key = m_simpleKeys.top();
    return key.flowLevel == GetFlowLevel();
}

// InsertPotentialSimpleKey
// . If we can, add a potential simple key to the queue,
//   and save it on a stack.
void Scanner::InsertPotentialSimpleKey() {
    if (!CanInsertPotentialSimpleKey())
        return;

    SimpleKey key(INPUT.mark(), GetFlowLevel());

    // first add a map start, if necessary
    if (InBlockContext()) {
        key.pIndent = PushIndentTo(INPUT.column(), IndentMarker::MAP);
        if (key.pIndent) {
            key.pIndent->status = IndentMarker::UNKNOWN;
            key.pMapStart = key.pIndent->pStartToken;
            key.pMapStart->status = Token::UNVERIFIED;
        }
    }

    // then add the (now unverified) key
    m_tokens.push(Token(Token::KEY, INPUT.mark()));
    key.pKey = &m_tokens.back();
    key.pKey->status = Token::UNVERIFIED;

    m_simpleKeys.push(key);
}

// InvalidateSimpleKey
// . Automatically invalidate the simple key in our flow level
void Scanner::InvalidateSimpleKey() {
    if (m_simpleKeys.empty())
        return;

    // grab top key
    SimpleKey& key = m_simpleKeys.top();
    if (key.flowLevel != GetFlowLevel())
        return;

    key.Invalidate();
    m_simpleKeys.pop();
}

// VerifySimpleKey
// . Determines whether the latest simple key to be added is valid,
//   and if so, makes it valid.
bool Scanner::VerifySimpleKey() {
    if (m_simpleKeys.empty())
        return false;

    // grab top key
    SimpleKey key = m_simpleKeys.top();

    // only validate if we're in the correct flow level
    if (key.flowLevel != GetFlowLevel())
        return false;

    m_simpleKeys.pop();

    bool isValid = true;

    // needs to be less than 1024 characters and inline
    if (INPUT.line() != key.mark.line || INPUT.pos() - key.mark.pos > 1024)
        isValid = false;

    // invalidate key
    if (isValid)
        key.Validate();
    else
        key.Invalidate();

    return isValid;
}

void Scanner::PopAllSimpleKeys() {
    while (!m_simpleKeys.empty())
        m_simpleKeys.pop();
}
} // namespace YAML
